package geppetto.cat.models.stats;


import geppetto.cat.common.Pair;
import geppetto.cat.corpus.BilingualCorpus;
import geppetto.cat.models.SparseTranslationTable;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;



public class SparseTranslationTableStats {

	public double meanEntropy(BilingualCorpus corpus, SparseTranslationTable tt) {
		double entropy = 0;
		for (int id = 0; id < corpus.getSourceSize(); id++) {
			entropy += entropy(id, tt);
		}
		return entropy / corpus.getSourceSize();
	}

	// Return the entropy by buckets of number of occurunces of source word
	public double[] entropyByCounts(BilingualCorpus corpus,
			SparseTranslationTable tt) {

		// Mean entropy by buckets
		// 0 -- c < 5 count
		// 5 -- 10 < c < 10
		// 10 -- > 30
		// > 30
		double entropy[] = new double[4];
		int counts[] = new int[4];
		System.out.println("Starting anaylising entroy");
		for (int id = 0; id < corpus.getSourceSize(); id++) {
			int scounts = corpus.getSourceWordCounts(id);
			if (scounts <= 5) {
				entropy[0] += entropy(id, tt);
				counts[0]++;
			} else if (scounts <= 10) {
				entropy[1] += entropy(id, tt);
				counts[1]++;
			} else if (scounts <= 30) {
				entropy[2] += entropy(id, tt);
				counts[2]++;
			} else {
				entropy[3] += entropy(id, tt);
				counts[3]++;
			}

		}
		for (int i = 0; i < entropy.length; i++) {
			entropy[i] = entropy[i] / counts[i];
		}
		System.out.println("Ended anaylising entroy");
		return entropy;
	}
	
	class CompareEntropy implements Comparator{

		public int compare(Object arg0, Object arg1) {
			if(arg0 instanceof Pair && arg1 instanceof Pair){
				if(((Double)((Pair)arg0).second()) > ((Double)((Pair)arg1).second())){
					return -1;
				}else{
					return 1;
				}
			}else{
				return 0;
			}
		}
		
	}
	public  ArrayList<Pair> getEntropy(BilingualCorpus corpus, SparseTranslationTable tt){
		ArrayList<Pair> entropy = new ArrayList<Pair>();
		for(int i = 0; i < corpus.getSourceSize(); i++){
			entropy.add(new Pair(i,entropy(i,tt)));
		}
		Collections.sort(entropy,new CompareEntropy());
		return entropy;
	}
	
	public double entropy(int sourceId, SparseTranslationTable tt) {
		// System.out.print(sourceId + " ");
		double entropy = 0;
		// Gets all entries for T(f|sourceId)
		double[] probs = tt.getProbabilitiesForSource(sourceId);
		double sum = 0;
		for (int i = 0; i < probs.length; i++) {
			// TODO should be log2
			// System.out.println(probs[i] + Math.log(i)/Math.log(2));
			double prob = probs[i];
			sum +=prob;
			if (prob != 0) {
				entropy += probs[i] * Math.log(probs[i]) / Math.log(2);
			}

		}
		if(Math.abs(sum-1) > 1.e-10){
			System.out.println("Probability is not summing to one " + sum);
			System.exit(-1);
		}
		entropy *= -1;
		// System.out.println(sourceId + " " + entropy);
		return entropy;
	}

	
	public static int[] getTopSourceDictionaryEntries(int sourceWordId, int top, SparseTranslationTable table, BilingualCorpus corpus) throws UnsupportedEncodingException{
		ArrayList<Pair> words = table.getForeingWords(sourceWordId);
		int[] topw = new int[top];
		
		for(int i =0; (i < words.size() && i < top); i++){
			System.out.println(" " + new String(corpus.getSourceWordById(sourceWordId).getBytes("utf8")) + " "+ corpus.getSourceWordCounts(sourceWordId) + " " +  new String(corpus.getForeignWordById(((Integer)words.get(i).first())).getBytes("utf8")) + " " + words.get(i).second());
			topw[i]=((Integer)words.get(i).first());
		}
		System.out.println();
		return topw;
	}
	
	public int[][] getEntropyExtremes(int top, SparseTranslationTable tt, BilingualCorpus corpus) throws UnsupportedEncodingException{
		int[][] extremes = new int[2][top];
		ArrayList<Pair> entropy = getEntropy(corpus, tt);
		System.out.println("Bigger entropy");
		for(int i = 0; i < top; i++){
			int wordId = (Integer) entropy.get(i).first();
			double v = (Double) entropy.get(i).second();
			System.out.println(new String(corpus.getSourceWordById(wordId).getBytes("utf8")) + " "+ corpus.getSourceWordCounts(wordId)  + " " +  v);
			extremes[0][i]=wordId;
		}
		System.out.println();
		System.out.println("Smaller entropy");
		for(int i = 0; i < top; i++){

			int wordId = (Integer) entropy.get(corpus.getSourceSize()-i-1).first();
			double v = (Double) entropy.get(corpus.getSourceSize()-i-1).second();
			System.out.println(new String(corpus.getSourceWordById(wordId).getBytes("utf8")) + " "+ corpus.getSourceWordCounts(wordId) + " " +  v);
			extremes[1][i]=wordId;
		}
		System.out.println();
		return extremes;
	}
	
	
	public static void main(String[] args) throws IOException {
		int size = Integer.parseInt(args[1]);
		SparseTranslationTableStats stats = new SparseTranslationTableStats();
		BilingualCorpus corpus = BilingualCorpus.getCorpusFromFileDescription(
				args[0], size,40);
		BilingualCorpus revCorpus = corpus.reverse();
		String baseDir = args[2];
		SparseTranslationTable tableF = SparseTranslationTable
				.LoadTranslationTable(corpus, baseDir+"forward");
		SparseTranslationTable tableB = SparseTranslationTable
		.LoadTranslationTable(revCorpus, baseDir+"backward");
	//	tableF.checkIfNormalized();
	//	tableB.checkIfNormalized();
		double[] counts = stats.entropyByCounts(corpus, tableF);
		System.out.println("Forward");
		System.out.println("<5\t<10\t<30\t>30");
		System.out.println(counts[0] + "\t" + counts[1] + "\t" + counts[2] + "\t" + counts[3]);
		counts = stats.entropyByCounts(revCorpus, tableB);
		System.out.println("Backward");
		System.out.println("<5\t<10\t<30\t>30");
		System.out.println(counts[0] + "\t" + counts[1] + "\t" + counts[2] + "\t" + counts[3]);
		
		System.out.println("Forward");
		int[][] extremes = stats.getEntropyExtremes(10, tableF, corpus);
		
		for(int i = 0; i < 10; i++){
			getTopSourceDictionaryEntries(extremes[0][i], 10, tableF, corpus);
		}
		System.out.println();
		for(int i = 0; i < 10; i++){
			getTopSourceDictionaryEntries(extremes[1][i], 10, tableF, corpus);
		}
		
		System.out.println("Backward");
		extremes = stats.getEntropyExtremes(10, tableB, revCorpus);
		
		for(int i = 0; i < 10; i++){
			getTopSourceDictionaryEntries(extremes[0][i], 10, tableB, revCorpus);
		}
		System.out.println();
		for(int i = 0; i < 10; i++){
			getTopSourceDictionaryEntries(extremes[1][i], 10, tableB, revCorpus);
		}
		
		
		
//		System.out.println("Entropy counts < 5 " + counts[0]);
//		System.out.println("Entropy counts < 10 " + counts[1]);
//		System.out.println("Entropy counts < 30 " + counts[2]);
//		System.out.println("Entropy counts > 30 " + counts[3]);

	}

}
